"""
Python集合(set)教学

集合是Python中的一种无序、可变的数据结构，其中的元素不重复。
集合使用花括号{}表示（但空集合需要使用set()创建，因为{}表示空字典）。 -> 需要跟字典区分开来，字典内部是键值对，集合内部是一个个元素，看内部成员可以知道是哪种
集合主要用于：去重、关系测试（如交集、并集）和数学集合运算。
"""

# =============== 1. 集合的创建 ===============

print("="*50)
print("1. 集合的创建")
print("="*50)

# 创建空集合
# 注意：不能使用empty_set = {} 创建空集合，这会创建一个空字典
empty_set = set()
print(f"空集合: {empty_set}, 类型: {type(empty_set)}")

# 创建包含元素的集合
fruits = {"苹果", "香蕉", "橙子", "葡萄"}
print(f"水果集合: {fruits}")

# 列表转换为集合
# 列表可能包含重复元素，但转换为集合后，重复元素会被去除
numbers_list = [1, 2, 3, 2, 4, 3, 5]
numbers_set = set(numbers_list)
print(f"原始列表: {numbers_list}")
print(f"转换后的集合: {numbers_set}")  # 重复的2和3被去除

# 字符串转换为集合
# 每个字符将成为集合中的一个元素
letters_set = set("hello")
print(f"从字符串'hello'创建的集合: {letters_set}")  # 注意'l'只出现一次，且顺序可能变化

# 使用集合推导式创建集合
squares_set = {x**2 for x in range(1, 6)}
# 区别一下我们前面学过的字典推导式，字典推导式例子：
squares_dict = {x: x**2 for x in range(1, 6)}
print(f"使用字典推导式创建的平方数字典: {squares_dict}")
print(f"使用集合推导式创建的平方数集合: {squares_set}")


# =============== 2. 集合的特点 ===============

print("\n"+"="*50)
print("2. 集合的特点：无序、唯一、可变")
print("="*50)

# 无序性 - 集合中的元素没有固定的顺序
# 你不能通过索引访问集合中的元素，因为集合是无序的
try:
    print(fruits[0])
except TypeError as e:
    print(f"\n错误信息: {e}")

# 唯一性 - 集合不能包含重复元素
print("\n集合中的元素是唯一的:")
duplicated_list = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_set = set(duplicated_list)
print(f"原始列表(有重复): {duplicated_list}")
print(f"转换为集合(无重复): {unique_set}")

# 可变性 - 集合可以添加或删除元素
print("\n集合是可变的，可以添加或删除元素:")
animals = {"猫", "狗", "兔子"}
print(f"原始集合: {animals}")
animals.add("鸟")
print(f"添加'鸟'后: {animals}")

# 注意：集合中的元素必须是不可变的(hashable)，因此不能包含列表或字典
# 尝试创建包含可变元素的集合会引发错误
try:
    invalid_set = {1, [2, 3], 4}  # 这会引发错误，因为[2, 3]是列表(可变的)
except TypeError as e:
    print(f"\n错误信息: {e}")

# 但集合可以包含元组，因为元组是不可变的
valid_set = {1, (2, 3), 4}
print(f"包含元组的有效集合: {valid_set}")


# =============== 3. 添加和删除元素 ===============

print("\n"+"="*50)
print("3. 添加和删除元素")
print("="*50)

# 创建示例集合
technologies = {"Python", "Java", "HTML", "CSS"}
print(f"原始技术集合: {technologies}")

# 添加单个元素
technologies.add("JavaScript")
print(f"添加'JavaScript'后: {technologies}")

# 尝试添加已存在的元素（不会有变化）
technologies.add("Python")
print(f"尝试添加已存在的'Python'后: {technologies}")  # 集合不变

# 添加多个元素
technologies.update(["SQL", "Docker", "Git"])
print(f"添加多个元素后: {technologies}")

# 也可以用update添加另一个集合的元素
more_techs = {"React", "Node.js"}
technologies.update(more_techs)
print(f"合并另一个集合后: {technologies}")

# 删除元素的方法
# 1. remove() - 删除指定元素，如果元素不存在则引发错误
technologies.remove("Java")
print(f"移除'Java'后: {technologies}")

# 尝试移除不存在的元素
try:
    technologies.remove("C++")  # 这会引发错误
except KeyError as e:
    print(f"移除不存在元素的错误: {e}")

# 2. discard() - 删除指定元素，如果元素不存在则不做任何操作，因此更推荐这个方法，代码更加安全和健壮
technologies.discard("HTML")
print(f"使用discard移除'HTML'后: {technologies}")

technologies.discard("C++")  # 不会引发错误
print(f"使用discard移除不存在的'C++'后: {technologies}")  # 集合不变

# 3. pop() - 随机移除并返回一个元素
removed = technologies.pop()
print(f"使用pop()移除的元素: {removed}")
print(f"移除后的集合: {technologies}")

# 4. clear() - 移除所有元素
technologies.clear()
print(f"清空后的集合: {technologies}")


# =============== 4. 成员检查（in 和 not in） ===============

print("\n"+"="*50)
print("4. 成员检查（in 和 not in）")
print("="*50)

# 创建示例集合
prime_numbers = {2, 3, 5, 7, 11, 13, 17, 19}
print(f"质数集合: {prime_numbers}")

# 检查元素是否在集合中
print(f"5是质数吗? {5 in prime_numbers}")
print(f"4是质数吗? {4 in prime_numbers}")

# 检查元素是否不在集合中
print(f"6不是质数吗? {6 not in prime_numbers}")
print(f"11不是质数吗? {11 not in prime_numbers}")

# 在条件语句中使用成员检查
number = 13
if number in prime_numbers:
    print(f"{number}是质数")
else:
    print(f"{number}不是质数")

# 集合成员检查的效率很高，尤其是对大型数据集
# 这是因为集合使用哈希表实现，成员检查的时间复杂度为O(1)


# =============== 5. 集合运算（交集、并集、差集） ===============

print("\n"+"="*50)
print("5. 集合运算（交集、并集、差集）")
print("="*50)

# 创建两个示例集合
set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}
print(f"集合A: {set_a}")
print(f"集合B: {set_b}")

# 交集：两个集合中共有的元素
# 方法1：使用 & 运算符
intersection = set_a & set_b
print(f"A ∩ B (使用&): {intersection}")

# 方法2：使用 intersection() 方法
intersection2 = set_a.intersection(set_b)
print(f"A ∩ B (使用intersection()): {intersection2}")

# 并集：合并两个集合的所有元素，去除重复
# 方法1：使用 | 运算符
union = set_a | set_b
print(f"A ∪ B (使用|): {union}")

# 方法2：使用 union() 方法
union2 = set_a.union(set_b)
print(f"A ∪ B (使用union()): {union2}")

# 差集：在A中但不在B中的元素
# 方法1：使用 - 运算符
difference = set_a - set_b
print(f"A - B (使用-): {difference}")

# 方法2：使用 difference() 方法
difference2 = set_a.difference(set_b)
print(f"A - B (使用difference()): {difference2}")

# B - A：在B中但不在A中的元素
difference_reverse = set_b - set_a
print(f"B - A: {difference_reverse}")

# 对称差集：在A或B中，但不同时在A和B中的元素
# 方法1：使用 ^ 运算符
symmetric_diff = set_a ^ set_b
print(f"A △ B (使用^): {symmetric_diff}")

# 方法2：使用 symmetric_difference() 方法
symmetric_diff2 = set_a.symmetric_difference(set_b)
print(f"A △ B (使用symmetric_difference()): {symmetric_diff2}")

# 子集和超集检查
subset_example = {1, 2}
print(f"{subset_example}是{set_a}的子集吗? {subset_example.issubset(set_a)}")
print(f"{set_a}是{subset_example}的超集吗? {set_a.issuperset(subset_example)}")


# =============== 6. 集合元素个数 ===============

print("\n"+"="*50)
print("6. 集合元素个数")
print("="*50)

# 使用len()函数获取集合元素个数
planets = {"水星", "金星", "地球", "火星", "木星", "土星", "天王星", "海王星"}
print(f"太阳系行星: {planets}")
print(f"行星数量: {len(planets)}")

# 空集合的长度为0
empty = set()
print(f"空集合的长度: {len(empty)}")

# 合并重复元素后的长度
duplicates = [1, 2, 3, 1, 2, 3, 1, 2, 3]
unique = set(duplicates)
print(f"原始列表: {duplicates}, 长度: {len(duplicates)}")
print(f"去重后集合: {unique}, 长度: {len(unique)}")


# =============== 7. 实际案例：多选题答案判定 ===============

print("\n"+"="*50)
print("7. 实际案例：多选题答案判定")
print("="*50)

def check_multi_choice_answer(correct_answer, student_answer):
    """
    检查多选题答案是否正确
    
    参数:
    - correct_answer: 正确答案（字符串，如"ABC"）
    - student_answer: 学生答案（字符串，如"BCA"）
    
    返回:
    - 布尔值，表示答案是否正确
    """
    # 将答案字符串转换为集合，这样顺序就不重要了
    correct_set = set(correct_answer)
    student_set = set(student_answer)
    
    # 比较两个集合是否相等
    return correct_set == student_set

# 创建一个多选题测验
multi_choice_test = [
    {"question": "以下哪些是Python的数据类型？（多选）",
     "options": {"A": "整数", "B": "字符串", "C": "列表", "D": "表格"},
     "correct": "ABC"},
    
    {"question": "以下哪些是编程语言？（多选）",
     "options": {"A": "HTML", "B": "CSS", "C": "JavaScript", "D": "PhotoShop"},
     "correct": "AC"},
    
    {"question": "以下哪些国家在亚洲？（多选）",
     "options": {"A": "中国", "B": "日本", "C": "法国", "D": "印度"},
     "correct": "ABD"}
]

# 模拟学生答案
student_answers = ["BAC", "CA", "ABDC"]

# 评分
score = 0
for i, question in enumerate(multi_choice_test):
    correct = question["correct"]
    student = student_answers[i]
    
    # 打印题目和答案对比
    print(f"\n问题 {i+1}: {question['question']}")
    print(f"选项: {question['options']}")
    print(f"正确答案: {correct}")
    print(f"学生答案: {student}")
    
    # 检查答案
    if check_multi_choice_answer(correct, student):
        result = "正确"
        score += 1
    else:
        result = "错误"
    
    print(f"判定: {result}")
    print(f"解释: 正确答案集合={set(correct)}, 学生答案集合={set(student)}")

# 打印最终得分
print(f"\n最终得分: {score}/{len(multi_choice_test)}")

print("\nPython集合学习结束！集合是一种强大的数据结构，特别适合需要去重和集合运算的场景。")
